/**
 * @file
 *
 * @ingroup ScoreCPU
 *
 * @brief Epiphany exception support implementation.
 */

/*
 * Copyright (c) 2015 University of York.
 * Hesham ALMatary <hmka501@york.ac.uk>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems/score/cpu.h>

#include <rtems/asm.h>
#include <rtems/score/percpu.h>

EXTERN(bsp_start_vector_table_begin)
EXTERN(_Thread_Dispatch)
PUBLIC(_ISR_Handler)

.section .text, "ax"
.align 4
TYPE_FUNC(_ISR_Handler)
SYM(_ISR_Handler):
  /* Reserve space for CPU_Exception_frame */
  sub sp, sp, #(CPU_EXCEPTION_FRAME_SIZE)

  str r0,  [sp]
  str r1,  [sp,1]
  str r2,  [sp,2]
  str r3,  [sp,3]
  str r4,  [sp,4]
  str r5,  [sp,5]
  str r6,  [sp,6]
  str r7,  [sp,7]
  str r8,  [sp,8]
  str r9,  [sp,9]
  str r10, [sp,10]
  str fp,  [sp,11]
  str r12, [sp,12]

  /* Save interrupted task stack pointer */
  add r1, sp, #(CPU_EXCEPTION_FRAME_SIZE + 8)
  str r1,[sp,13]

  str lr,  [sp,14]
  str r15, [sp,15]
  str r16, [sp,16]
  str r17, [sp,17]
  str r18, [sp,18]
  str r19, [sp,19]
  str r20, [sp,20]
  str r21, [sp,21]
  str r22, [sp,22]
  str r23, [sp,23]
  str r24, [sp,24]
  str r25, [sp,25]
  str r26, [sp,26]
  str r27, [sp,27]
  str r28, [sp,28]
  str r29, [sp,29]
  str r30, [sp,30]
  str r31, [sp,31]
  str r32, [sp,32]
  str r33, [sp,33]
  str r34, [sp,34]
  str r35, [sp,35]
  str r36, [sp,36]
  str r37, [sp,37]
  str r38, [sp,38]
  str r39, [sp,39]
  str r40, [sp,40]
  str r41, [sp,41]
  str r42, [sp,42]
  str r43, [sp,43]
  str r44, [sp,44]
  str r45, [sp,45]
  str r46, [sp,46]
  str r47, [sp,47]
  str r48, [sp,48]
  str r49, [sp,49]
  str r50, [sp,50]
  str r51, [sp,51]
  str r52, [sp,52]
  str r53, [sp,53]
  str r54, [sp,54]
  str r55, [sp,55]
  str r56, [sp,56]
  str r57, [sp,57]
  str r58, [sp,58]
  str r59, [sp,59]
  str r60, [sp,60]
  str r61, [sp,61]
  /* r62 and r63 are saved from start.S interrupt entry
   * and hold vector number and _ISR_Handler address repsectively.
   */

  /* Save status register */
  movfs r1,status
  str r1, [sp,62]

  /* Save config register */
  movfs r1,config
  str r1, [sp,63]

  /* Save interrupt return address register */
  movfs r1,iret
  str r1, [sp,64]

  mov  r33, %low(_Per_CPU_Information)
  movt r33, %high(_Per_CPU_Information)

       add  r6, r33, #(PER_CPU_ISR_NEST_LEVEL)
       add  r8, r33, #(PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL)

  /* Increment nesting level and disable thread dispatch */
  ldr  r5, [r6]
  ldr  r7, [r8]
  add  r5, r5, #1
  add  r7, r7, #1
  str  r5, [r6]
  str  r7, [r8]

  /* Keep sp (Exception frame address) in r32 - Callee saved */
  mov  r32, sp

  /* Keep _Per_CPU_Information address in r33 - Callee saved */
  mov  r33, r18

  /* Call the exception handler from vector table.
   * First function arg for C handler is vector number,
   * and the second is a pointer to exception frame.
   */
  mov  r0,  r62
  mov  r1,  sp

  mov  r27, r62
  lsl  r27, r27, #2
  mov  r26, %low(bsp_start_vector_table_begin)
  movt r15, #0
  add  r27, r27, r26
  ldr  r27, [r27]

  /* Do not switch stacks if we are in a nested interrupt. At
   * this point r5 should be holding ISR_NEST_LEVEL value.
   */
  sub  r37, r5, #1
  bgtu jump_to_c_handler

  /* Switch to RTEMS dedicated interrupt stack */
  add     sp, r18, #(PER_CPU_INTERRUPT_STACK_HIGH)
  ldr     sp, [sp]

jump_to_c_handler:
  jalr r27

  /* Switch back to the interrupted task stack */
  mov  sp, r32

  /* Get the address of _Per_CPU_Information */
  mov r18, r33

  /* Decrement nesting level and enable multitasking */
  add  r6, r18, #(PER_CPU_ISR_NEST_LEVEL)
       add  r8, r18, #(PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL)

  ldr     r5, [r6]
  ldr     r7, [r8]
  sub     r5, r5, #1
  sub     r7, r7, #1
  str     r5, [r6]
  str     r7, [r8]

  /* Check if _ISR_Nest_level > 0 */
  sub     r37, r5, #0
  bgtu    exception_frame_restore

  /* Check if _Thread_Dispatch_disable_level > 0 */
  sub     r37, r7, #0
  bgtu    exception_frame_restore

  /* Check if dispatch needed */
  add     r31, r18, #(PER_CPU_DISPATCH_NEEDED)
  ldr     r31, [r31]

  sub     r35, r31, #0
  beq     exception_frame_restore

  mov     r35, %low(_Thread_Dispatch)
  movt    r35, %high(_Thread_Dispatch)
  jalr    r35

exception_frame_restore:

  ldr r1,  [sp,1]
  ldr r2,  [sp,2]
  ldr r3,  [sp,3]
  ldr r4,  [sp,4]
  ldr r5,  [sp,5]
  ldr r6,  [sp,6]
  ldr r7,  [sp,7]
  ldr r8,  [sp,8]
  ldr r9,  [sp,9]
  ldr r10, [sp,10]
  ldr fp,  [sp,11]
  ldr r12, [sp,12]
  ldr lr,  [sp,14]
  ldr r15, [sp,15]
  ldr r16, [sp,16]
  ldr r17, [sp,17]
  ldr r18, [sp,18]
  ldr r19, [sp,19]
  ldr r20, [sp,20]
  ldr r21, [sp,21]
  ldr r22, [sp,22]
  ldr r23, [sp,23]
  ldr r24, [sp,24]
  ldr r25, [sp,25]
  ldr r26, [sp,26]
  ldr r27, [sp,27]
  ldr r28, [sp,28]
  ldr r29, [sp,29]
  ldr r30, [sp,30]
  ldr r31, [sp,31]
  ldr r32, [sp,32]
  ldr r34, [sp,34]
  ldr r36, [sp,36]
  ldr r38, [sp,38]
  ldr r39, [sp,39]
  ldr r40, [sp,40]
  ldr r41, [sp,41]
  ldr r42, [sp,42]
  ldr r43, [sp,43]
  ldr r44, [sp,44]
  ldr r45, [sp,45]
  ldr r46, [sp,46]
  ldr r47, [sp,47]
  ldr r48, [sp,48]
  ldr r49, [sp,49]
  ldr r50, [sp,50]
  ldr r51, [sp,51]
  ldr r52, [sp,52]
  ldr r53, [sp,53]
  ldr r54, [sp,54]
  ldr r55, [sp,55]
  ldr r56, [sp,56]
  ldr r57, [sp,57]
  ldr r58, [sp,58]
  ldr r59, [sp,59]
  ldr r60, [sp,60]
  ldr r61, [sp,61]

  /* Restore status register */
  ldr r0,[sp,62]
  movts status, r0

  /* Restore config register */
  ldr r0, [sp,63]
  movts config, r0

  /* Restore interrupt return address register */
  ldr   r0, [sp,64]
  movts iret, r0

  ldr r0,[sp]

  /* Restore interrupted task's stack pointer */
  ldr sp, [sp,13]

  /* r62 and r63 are saved from start.S interrupt entry
   * and hold vector number and _ISR_Handler address repsectively.
   */
  ldr r62, [sp, -8]
  ldr r63, [sp, -4]

  /* return from interrupt */
  rti
